/*
 * rtc.c
 *
 *  Created on: Aug 26, 2025
 *      Author: Young
 */
#include "rtc.h"
#include <time.h>

extern RTC_HandleTypeDef hrtc;

DateTime_TypeDef dateTime;

/**
 * 判断闰年
 *
 * @param
 * @return
 */
uint8_t isLeapYear(int year) {
	// 四年一闰，百年不闰，四百年再闰
	if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
		return 1;
	else
		return 0;
}

/**
 * 计算星期
 *
 * @param
 * @return
 */
uint8_t rtcCalculateWeek(uint16_t _year, uint8_t _month, uint8_t _date) {
	/*
	 蔡勒（Zeller）公式
	 历史上的某一天是星期几？未来的某一天是星期几？关于这个问题，有很多计算公式（两个通用计算公式和
	 一些分段计算公式），其中最著名的是蔡勒（Zeller）公式。
	 即w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1

	 公式中的符号含义如下，
	 w：星期；
	 c：年的高2位，即世纪-1
	 y：年（两位数）；
	 m：月（m大于等于3，小于等于14，即在蔡勒公式中，某年的1、2月要看作上一年的13、14月来计算，
	 比如2003年1月1日要看作2002年的13月1日来计算）；
	 d：日；
	 [ ]代表取整，即只要整数部分。

	 算出来的W除以7，余数是几就是星期几。如果余数是0，则为星期日。
	 如果结果是负数，负数求余数则需要特殊处理：
	 负数不能按习惯的余数的概念求余数，只能按数论中的余数的定义求余。为了方便
	 计算，我们可以给它加上一个7的整数倍，使它变为一个正数，然后再求余数

	 以2049年10月1日（100周年国庆）为例，用蔡勒（Zeller）公式进行计算，过程如下：
	 蔡勒（Zeller）公式：w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1
	 =49+[49/4]+[20/4]-2×20+[26× (10+1)/10]+1-1
	 =49+[12.25]+5-40+[28.6]
	 =49+12+5-40+28
	 =54 (除以7余5)
	 即2049年10月1日（100周年国庆）是星期5。
	 */
	uint8_t y, c, m, d;
	int16_t w;

	if (_month >= 3) {
		m = _month;
		y = _year % 100;
		c = _year / 100;
		d = _date;
	} else /* 某年的1、2月要看作上一年的13、14月来计算 */
	{
		m = _month + 12;
		y = (_year - 1) % 100;
		c = (_year - 1) / 100;
		d = _date;
	}
	w = y + y / 4 + c / 4 - 2 * c + ((uint16_t) 26 * (m + 1)) / 10 + d - 1;
	if (w == 0) {
		//w = 7;	/* 表示周日 */
	} else if (w < 0) /* 如果w是负数，则计算余数方式不同 */
	{
		w = 7 - (-w) % 7;
	} else {
		w = w % 7;
	}
	return w;
}

/**
 * @brief  Read the time counter available in RTC_CNT registers.
 * @param  hrtc   pointer to a RTC_HandleTypeDef structure that contains
 *                the configuration information for RTC.
 * @retval Time counter
 */
static uint32_t RTC_ReadTimeCounter(RTC_HandleTypeDef *hrtc) {
	uint16_t high1 = 0U, high2 = 0U, low = 0U;
	uint32_t timecounter = 0U;

	high1 = READ_REG(hrtc->Instance->CNTH & RTC_CNTH_RTC_CNT);
	low = READ_REG(hrtc->Instance->CNTL & RTC_CNTL_RTC_CNT);
	high2 = READ_REG(hrtc->Instance->CNTH & RTC_CNTH_RTC_CNT);

	if (high1 != high2) {
		/* In this case the counter roll over during reading of CNTL and CNTH registers,
		 read again CNTL register then return the counter value */
		timecounter = (((uint32_t) high2 << 16U)
				| READ_REG(hrtc->Instance->CNTL & RTC_CNTL_RTC_CNT));
	} else {
		/* No counter roll over during reading of CNTL and CNTH registers, counter
		 value is equal to first value of CNTL and CNTH */
		timecounter = (((uint32_t) high1 << 16U) | low);
	}

	return timecounter;
}

/**
 * @brief  Enters the RTC Initialization mode.
 * @param  hrtc   pointer to a RTC_HandleTypeDef structure that contains
 *                the configuration information for RTC.
 * @retval HAL status
 */
static HAL_StatusTypeDef RTC_EnterInitMode(RTC_HandleTypeDef *hrtc) {
	uint32_t tickstart = 0U;

	tickstart = HAL_GetTick();
	/* Wait till RTC is in INIT state and if Time out is reached exit */
	while ((hrtc->Instance->CRL & RTC_CRL_RTOFF) == (uint32_t) RESET) {
		if ((HAL_GetTick() - tickstart) > RTC_TIMEOUT_VALUE) {
			return HAL_TIMEOUT;
		}
	}

	/* Disable the write protection for RTC registers */
	__HAL_RTC_WRITEPROTECTION_DISABLE(hrtc);

	return HAL_OK;
}

/**
 * @brief  Exit the RTC Initialization mode.
 * @param  hrtc   pointer to a RTC_HandleTypeDef structure that contains
 *                the configuration information for RTC.
 * @retval HAL status
 */
static HAL_StatusTypeDef RTC_ExitInitMode(RTC_HandleTypeDef *hrtc) {
	uint32_t tickstart = 0U;

	/* Disable the write protection for RTC registers */
	__HAL_RTC_WRITEPROTECTION_ENABLE(hrtc);

	tickstart = HAL_GetTick();
	/* Wait till RTC is in INIT state and if Time out is reached exit */
	while ((hrtc->Instance->CRL & RTC_CRL_RTOFF) == (uint32_t) RESET) {
		if ((HAL_GetTick() - tickstart) > RTC_TIMEOUT_VALUE) {
			return HAL_TIMEOUT;
		}
	}

	return HAL_OK;
}

/**
 * @brief  Write the time counter in RTC_CNT registers.
 * @param  hrtc   pointer to a RTC_HandleTypeDef structure that contains
 *                the configuration information for RTC.
 * @param  TimeCounter: Counter to write in RTC_CNT registers
 * @retval HAL status
 */
static HAL_StatusTypeDef RTC_WriteTimeCounter(RTC_HandleTypeDef *hrtc,
		uint32_t TimeCounter) {
	HAL_StatusTypeDef status = HAL_OK;

	/* Set Initialization mode */
	if (RTC_EnterInitMode(hrtc) != HAL_OK) {
		status = HAL_ERROR;
	} else {
		/* Set RTC COUNTER MSB word */
		WRITE_REG(hrtc->Instance->CNTH, (TimeCounter >> 16U));
		/* Set RTC COUNTER LSB word */
		WRITE_REG(hrtc->Instance->CNTL, (TimeCounter & RTC_CNTL_RTC_CNT));

		/* Wait for synchro */
		if (RTC_ExitInitMode(hrtc) != HAL_OK) {
			status = HAL_ERROR;
		}
	}

	return status;
}

/**
 * toDateTime
 *
 * @param
 * @return
 */
void toDateTime(DateTime_TypeDef *dateTime) {
	time_t timeStamp = dateTime->timeStamp + 3600 * TIME_ZONE;
	struct tm *timeptr = localtime(&timeStamp);
	dateTime->year = timeptr->tm_year + 1900;
	dateTime->month = timeptr->tm_mon + 1;
	dateTime->date = timeptr->tm_mday;
	dateTime->week = timeptr->tm_wday;
	dateTime->hour = timeptr->tm_hour;
	dateTime->minute = timeptr->tm_min;
	dateTime->second = timeptr->tm_sec;
}

/**
 * toTimeStamp
 *
 * @param
 * @return
 */
void toTimeStamp(DateTime_TypeDef *dateTime) {
	struct tm tmStruct;
	tmStruct.tm_year = dateTime->year - 1900;
	tmStruct.tm_mon = dateTime->month - 1;
	tmStruct.tm_mday = dateTime->date;
	tmStruct.tm_hour = dateTime->hour;
	tmStruct.tm_min = dateTime->minute;
	tmStruct.tm_sec = dateTime->second;
	time_t timeStamp = mktime(&tmStruct);
	dateTime->timeStamp = timeStamp - 3600 * TIME_ZONE;
}

/**
 * rtcSetup
 *
 * @param
 * @return
 */
void rtcSetup(void) {
	// 检查BKP寄存器
	if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) == 0xA5A5)
		return;
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0xA5A5);
	// 默认时间
	DateTime_TypeDef dateTime = { .year = 2021, .month = 2, .date = 20, .hour =
			9, .minute = 0, .second = 0 };
	// 转成时间戳
	toTimeStamp(&dateTime);
	// 修改时钟
	RTC_WriteTimeCounter(&hrtc, dateTime.timeStamp);
}

/**
 * rtcLoop
 *
 * @param
 * @return
 */
void rtcLoop(void) {
	uint32_t timeStamp = RTC_ReadTimeCounter(&hrtc);
	DateTime_TypeDef dateTime = { .timeStamp = timeStamp };
	toDateTime(&dateTime);
	holdingRegister[0] = timeStamp;

	if (KEY_ENT) {
		// 默认时间
		DateTime_TypeDef dateTime = { .year = 2021, .month = 2, .date = 20,
				.hour = 7, .minute = 0, .second = 0 };
		// 转成时间戳
		toTimeStamp(&dateTime);
		// 修改时钟
		RTC_WriteTimeCounter(&hrtc, 1611174500);
	}
}
